package edu.uoc.tds.pec4.gestor.impl;

import edu.uoc.tds.pec4.model.SS1_CentroDocente;
import edu.uoc.tds.pec4.model.SS1_TipoEvento;
import edu.uoc.tds.pec4.model.SS1_Universidad;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import edu.uoc.tds.pec4.util.ExceptionUtils;
import java.io.File;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.text.SimpleDateFormat;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Tecnicas de Desarrollo de Software - (UOC)
 * Clase que implementa la interfice de SS4_GestorDisco
 * @author Pedro Bauzá Picó
 * @version 1.0
 */
public class GestorDiscoImpl extends UnicastRemoteObject {

    private static final long serialVersionUID = 1L;
    private Connection conexion;
    private Boolean conectado;
    final String propertiesFile = new File("").getAbsolutePath() + "/edu/uoc/tds/pec4/properties/jdbc.properties";
//    final String propertiesFile = new File("").getAbsolutePath() + "/src/edu/uoc/tds/pec4/properties/jdbc.properties";
    final String driver = "org.postgresql.Driver";
    final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

    /**
     * Construtor del objeto de la implementacion del remoto
     */
    public GestorDiscoImpl() throws RemoteException{
        conexion = null;
        conectado = null;
    }

    /**
     * Metodo de conexion a la base de datos
     * @return  Retorna verdadero si se ha establecido la conexion, sino retorna falso
     */
    public Boolean conectaBD() throws RemoteException {
        try {
            //Recoger el driver JDBC especifico de Postgre
            Class.forName(driver);
            //Recuperar información del fichero de propiedades
            Properties prop = new Properties();
            prop.load(new FileInputStream(propertiesFile));
            String url = prop.getProperty("url");
            String usuario = prop.getProperty("username");
            String clave = prop.getProperty("password");
            //Montar la conexion a la BBDD
            conexion = DriverManager.getConnection(url, usuario, clave);
            conectado = true;
            return true;
        } catch (ClassNotFoundException e) {
            ExceptionUtils.visualizarError(ExceptionUtils.Err_ClassPath_Postgre);
        } catch (FileNotFoundException e) {
            ExceptionUtils.visualizarError(ExceptionUtils.Err_No_Properties);
        } catch (IOException e) {
            ExceptionUtils.visualizarError(ExceptionUtils.Err_Incorrecto_Properties);
        } catch (SQLException e) {
            ExceptionUtils.visualizarError(ExceptionUtils.Err_BD);
        } catch (Exception e) {
            ExceptionUtils.visualizarError(ExceptionUtils.ERR_Inesperado);
        }
        return false;
    }

    /**
     * Metodo que cierra la conexion a la base de datos
     */
    public void desconectaBD() throws RemoteException {
        if (conectado) {
            try {
                conexion.close();
            } catch (Exception e) {
                 ExceptionUtils.visualizarError(ExceptionUtils.ERR_Inesperado);
            } finally {
                conectado = false;
            }
        }
    }

    /**
     * Metodo para hacer una consulta completa sobre una tabla
     * @param  p_NombreTabla Nombre de la tabla sobre la que se lanza la consulta
     * @return  Devuevle de ResulSet con los datos
     */
    public ResultSet select(String p_NombreTabla) throws RemoteException, SQLException, Exception {
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        ResultSet rs;
        try {
            Statement st = conexion.createStatement();
            String v_Sql = "SELECT * FROM " + p_NombreTabla;
            rs = st.executeQuery(v_Sql);
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return rs;
    }

    /**
     * Metodo para hacer una consulta sobre una tabla, indicando una condicion
     * @param  p_NombreTabla Nombre de la tabla sobre la que se lanza la consulta
     * @param  p_Condicion Condicion de la consulta
     * @return  Devuevle de ResulSet con los datos
     */
    public ResultSet select(String p_NombreTabla, String p_Condicion) throws RemoteException, SQLException, Exception {
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        ResultSet rs;
        try {
            Statement st = conexion.createStatement();
            String v_Sql = "SELECT * FROM " + p_NombreTabla;
            if (!p_Condicion.equals("")) {
                v_Sql = v_Sql + " WHERE " + p_Condicion;
            }
            rs = st.executeQuery(v_Sql);
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return rs;
    }

    /**
     * Metodo para hacer una consulta sobre una tabla, indicando una condicion y los campos a recuperar
     * @param  p_NombreTabla Nombre de la tabla sobre la que se lanza la consulta
     * @param  p_Condicion Condicion de la consulta
     * @param  p_Campos Campos a recuperar
     * @return  Devuevle de ResulSet con los datos
     */
    public ResultSet select(String p_NombreTabla, String p_Condicion, String p_Campos) throws RemoteException, SQLException, Exception {
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        ResultSet rs;
        try {
            Statement st = conexion.createStatement();
            String tmp = "SELECT " + p_Campos + " FROM " + p_NombreTabla;
            if (!p_Condicion.equals("")) {
                tmp = tmp + " WHERE " + p_Condicion;
            }
            rs = st.executeQuery(tmp);
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return (rs);
    }

    /**
     * Metodo para hacer una consulta completa sobre una tabla
     * @param  p_Consulta Contiene la consulta que se va a realizar
     * @return  Devuevle de ResulSet con los datos
     */
    public ResultSet selectQuery(String p_Consulta) throws RemoteException, SQLException, Exception {
        conectado = true;
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        ResultSet rs;
        try {
            Statement st = conexion.createStatement();
            rs = st.executeQuery(p_Consulta);
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return rs;
    }

    /**
     * Metodo para hacer un insert en base a consulta completa sobre una tabla
     * @param  p_Consulta Contiene la consulta que se va a realizar
     * @return  Devuevle boolean con el resultado de la ejecucion correcta / incorrecta
     */
    public boolean insertQuery(String p_Consulta) throws RemoteException, SQLException, Exception {
        conectado = true;
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        boolean result = true;
        try {
            Statement st = conexion.createStatement();
            result = st.execute(p_Consulta);
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return result;
    }

    /**
     * Metodo para hacer una insercion sobre una tabla, indicando una tabla y los valores
     * @param  p_NombreTabla Nombre de la tabla sobre la que se lanza la consulta
     * @param p_Valores Los valores que se utilizan para la insercion del registro
     * @return Devolvemos el valor que nos retorna executeUpdate, que 1 ha ido correcto y otro valor es que ha ido incorrecto
     */
    public int insert(String p_NombreTabla, String p_Valores) throws RemoteException, SQLException, Exception {
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        int valor = 0;
        try {
            Statement st = conexion.createStatement();
            valor = st.executeUpdate("INSERT INTO " + p_NombreTabla + " VALUES (" + p_Valores + ")");
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return valor;
    }

    /**
     * Metodo para hacer una insercion sobre una tabla, indicando una tabla, los campos y los valores
     * @param p_NombreTabla Nombre de la tabla sobre la que se lanza la consulta
     * @param p_Valores Los valores que se utilizan para la insercion del registro
     * @param p_Campos Los campos donde se insertaran los valores
     * @return Devolvemos el valor que nos retorna executeUpdate, que 1 ha ido correcto y otro valor es que ha ido incorrecto
     */
    public int insert(String p_NombreTabla, String p_Valores, String p_Campos) throws RemoteException, SQLException, Exception {
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        int valor = 0;

        try {
            Statement st = conexion.createStatement();
            valor = st.executeUpdate("INSERT INTO " + p_NombreTabla + " (" + p_Campos + ") VALUES (" + p_Valores + ")");
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return valor;
    }

    /**
     * Metodo para hacer una modificacion sobre una tabla, indicando una tabla y los campos
     * @param p_NombreTabla Nombre de la tabla sobre la que se lanza la consulta
     * @param p_Campos Los campos donde se insertaran los valores
     * @return Devolvemos el valor que nos retorna executeUpdate, que sera el numero de filas modificadas
     */
    public int update(String p_NombreTabla, String p_Campos) throws RemoteException, SQLException, Exception {
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        int valor = 0;
        try {
            Statement st = conexion.createStatement();
            valor = st.executeUpdate("UPDATE " + p_NombreTabla + " SET " + p_Campos);
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return valor;
    }

    /**
     * Metodo para hacer una modificacion sobre una tabla, indicando una tabla, los campos y las condiciones
     * @param p_NombreTabla Nombre de la tabla sobre la que se lanza la consulta
     * @param p_Campos Los campos donde se insertaran los valores
     * @param p_Condicion String que conté la condició per modificar.
     * @return Devolvemos el valor que nos retorna executeUpdate, que sera el numero de filas modificadas
     */
    public int update(String p_NombreTabla, String p_Condicion, String p_Campos) throws RemoteException, SQLException, Exception {
        if (!conectado) {
            throw new Exception(ExceptionUtils.Err_BD);
        }
        int valor = 0;
        try {
            String tmp = "UPDATE " + p_NombreTabla + " SET " + p_Campos;
            if (!p_Condicion.equals("")) {
                tmp = tmp + " WHERE " + p_Condicion;
            }
            Statement st = conexion.createStatement();
            valor = st.executeUpdate(tmp);
        } catch (SQLException e) {
            throw new SQLException(ExceptionUtils.ERR_SQL);
        }
        return valor;
    }

    /**COMBOS**/

    public List getComboUniversidades() throws RemoteException {
       List<SS1_Universidad> llista = new ArrayList<SS1_Universidad>();
        ResultSet rs = null;
        try {
            conectaBD();
            rs = select("universidad");
            while (rs.next()) {
                SS1_Universidad sS1_Universidad = new SS1_Universidad();
                sS1_Universidad.setNombre(rs.getString("nombre"));
                sS1_Universidad.setId(rs.getInt("cod_Universidad"));
                llista.add(sS1_Universidad);
            }
        } catch (SQLException e) {
            ExceptionUtils.visualizarError(e.getMessage());
        } catch (Exception e) {
            ExceptionUtils.visualizarError(e.getMessage());
        } finally {
            try {
                rs.close();
                desconectaBD();
            } catch (Exception e) {
                ExceptionUtils.visualizarError(e.getMessage());
            }
        }
        return llista;
    }

    /**
     * Metodo que consulta los tipos de eventos disponibles en la aplicacion
     * @return Lista de objetos tipo evento
     */
    public List<SS1_TipoEvento> getComboTipoEvento() throws RemoteException {
        List<SS1_TipoEvento> lista = new ArrayList<SS1_TipoEvento>();
        ResultSet rs = null;
        try {
            conectaBD();
            String v_Tabla = "tipoevento";
            String v_Condicion = "activo='1'";
            String v_Campos = "cod_tipoevento, titulo";
            rs = select(v_Tabla, v_Condicion, v_Campos);

            while (rs.next()) {
                SS1_TipoEvento tipoEvento = new SS1_TipoEvento();
                tipoEvento.setId(rs.getInt("cod_tipoevento"));
                tipoEvento.setTitulo(rs.getString("titulo"));
                lista.add(tipoEvento);
            }

        } catch (SQLException ex) {
            ExceptionUtils.visualizarError(ex.getMessage());
            lista = null;
        } catch (Exception ex) {
            ExceptionUtils.visualizarError(ex.getMessage());
            lista = null;
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                desconectaBD();
            } catch (Exception e) {
            }
        }
        return lista;
    }

    /**
     * Metodo que retorna clave,valor de centros docentes para llenar combo
     * @return Lista de objetos tipo evento
     */
    public List<SS1_CentroDocente> getComboCentroDocente() throws RemoteException {
        List<SS1_CentroDocente> lista = new ArrayList<SS1_CentroDocente>();
        ResultSet rs = null;
        try {
            conectaBD();
            String v_Tabla = "centrodocente";
            String v_Condicion = "activo='1'";
            String v_Campos = "cod_centrodocente, nombre";
            rs = select(v_Tabla, v_Condicion, v_Campos);

            while (rs.next()) {
                SS1_CentroDocente centrodocente = new SS1_CentroDocente();
                centrodocente.setId(rs.getInt("cod_centrodocente"));
                centrodocente.setNombre(rs.getString("nombre"));
                lista.add(centrodocente);
            }

        } catch (SQLException ex) {
            ExceptionUtils.visualizarError(ex.getMessage());
            lista = null;
        } catch (Exception ex) {
            ExceptionUtils.visualizarError(ex.getMessage());
            lista = null;
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                desconectaBD();
            } catch (Exception e) {
            }
        }
        return lista;
    }

    public List<String> getComboPaises() throws RemoteException, SQLException {
        List<String> paises = new ArrayList<String>();
        try {
            conectaBD();
            ResultSet rs = select("pais");

            while (rs.next()){
                paises.add(rs.getString("nombre"));
            }

            rs.close();
            desconectaBD();

        } catch (SQLException e) {
            ExceptionUtils.visualizarError(e.getMessage());
        } catch (Exception e){
            ExceptionUtils.visualizarError(e.getMessage());
        }
        return paises;
    }

    public Connection getConexion() {
        return conexion;
    }
}
